/*
 * Copyright (c) 2008-2009 Bill Whitacre http://rampancy.g0dsoft.com
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
*/

#ifndef RAT_PHYSICS_WORLD
#define RAT_PHYSICS_WORLD

#include "common.h"

#include "body.h"
#include "body_manager.h"
#include "body_manager_array_table.h"
#include "body_manager_quad_tree.h"

#include "collision_filter.h"

#include "constraint.h"
#include "spring.h"

#include "island.h"

typedef enum rat_integrator_type
{
	rat_integrator_null=0,
	rat_integrator_euler=1,
	rat_integrator_rk4=2
} rat_integrator_type;

struct rat_world;

typedef struct rat_world_callbacks
{
	void (*body_left_world)(struct rat_world *thisworld,rat_body *thisbody,void *userdata);
	void (*collision_occurs)(struct rat_world *thisworld,rat_arbiter *arbiter,void *userdata);
	void (*collision_persists)(struct rat_world *thisworld,rat_arbiter *arbiter,void *userdata);
	void (*collision_ending)(struct rat_world *thisworld,rat_arbiter *arbiter,void *userdata);

	void *body_left_world_userdata;
	void *collision_occurs_userdata;
	void *collision_persists_userdata;
	void *collision_ending_userdata;
} rat_world_callbacks;

typedef struct rat_world
{
	// special internal allocators
	rat_stack_allocator *sal;
	rat_block_allocator *bal;

	// world attributes
	aabb world_bounds;
	vector2 gravity;
	rat_real damping;

	// collision accuracy
	unsigned int elastic_iterations;
	unsigned int inelastic_iterations;
	rat_real bias_coef,elastic_coef;

	// pointer to integrator function (euler or runge kutta)
	void (*body_integrate_position)(rat_body *body,rat_real timestep);

	// world callbacks
	rat_world_callbacks *callbacks;

	// holds body data and collides them (broadphase where implemented)
	rat_body_manager *body_manager;

	// collision filters
	rat_collision_filter_list *filters;

	// constraints
	unsigned int numconsts,numconstsdel;
	unsigned int numsprings,numspringsdel;
	rat_constraint **constraints,**constsdel;
	rat_spring **springs,**springsdel;

	unsigned int numislands;
} rat_world;

#ifdef __cplusplus
extern "C" {
#endif

// default world event callbacks

void rat_default_body_left_world(rat_world *thisworld,rat_body *thisbody,void *userdata);
void rat_default_collision_occurs(rat_world *thisworld,rat_arbiter *arbiter,void *userdata);
void rat_default_collision_persists(rat_world *thisworld,rat_arbiter *arbiter,void *userdata);
void rat_default_collision_ending(rat_world *thisworld,rat_arbiter *arbiter,void *userdata);

// world stuff

// note that world bounds only effect spatial indexing methods
// and the leave world callback.
rat_world *rat_world_create(aabb world_bounds,rat_integrator_type integrator,
	unsigned int collision_iterations,rat_body_manager_type bmtype,void *bmdata);
void rat_world_destroy(rat_world *world);

void rat_world_set_body_left_world_callback(rat_world *world,void (*body_left_world)(struct rat_world *thisworld,rat_body *thisbody,void *userdata),void *userdata);
void rat_world_set_collision_occurs_callback(rat_world *world,void (*collision_occurs)(struct rat_world *thisworld,rat_arbiter *arbiter,void *userdata),void *userdata);
void rat_world_set_collision_persists_callback(rat_world *world,void (*collision_persists)(struct rat_world *thisworld,rat_arbiter *arbiter,void *userdata),void *userdata);
void rat_world_set_collision_ending_callback(rat_world *world,void (*collision_ending)(struct rat_world *thisworld,rat_arbiter *arbiter,void *userdata),void *userdata);

// free everything; convenience function for little stuff
void rat_world_free_entities(rat_world *world);

// awaken all bodies.  good for a gravity change, for example
void rat_world_awaken_all(rat_world *world);

void rat_world_add_body(rat_world *world,rat_body *body);
void rat_world_remove_body(rat_world *world,rat_body *body);
void rat_world_remove_and_destroy_body(rat_world *world,rat_body *body);
void rat_world_clear_bodies(rat_world *world);

void rat_world_add_constraint(rat_world *world,rat_constraint *constraint);
void rat_world_remove_constraint(rat_world *world,rat_constraint *constraint);
void rat_world_remove_and_destroy_constraint(rat_world *world,rat_constraint *constraint);
void rat_world_clear_constraints(rat_world *world);

void rat_world_add_spring(rat_world *world,rat_spring *spring);
void rat_world_remove_spring(rat_world *world,rat_spring *spring);
void rat_world_remove_and_destroy_spring(rat_world *world,rat_spring *spring);
void rat_world_clear_springs(rat_world *world);

void rat_world_set_gravity(rat_world *world,vector2 gravity);
void rat_world_set_damping(rat_world *world,rat_real damping);
void rat_world_set_solver_iterations(rat_world *world,unsigned int iterations);

vector2 rat_world_get_gravity(rat_world *world);
rat_real rat_world_get_damping(rat_world *world);
unsigned int rat_world_get_solver_iterations(rat_world *world);

void rat_world_add_collision_filter(rat_world *world,int tag_a,int tag_b,int atroot);
int rat_world_remove_collision_filter(rat_world *world,int tag_a,int tag_b,int fromlast);
void rat_world_clear_collision_filters(rat_world *world);

rat_iterator *rat_world_all_bodies_iterator(rat_world *world);
rat_iterator *rat_world_static_bodies_iterator(rat_world *world);
rat_iterator *rat_world_dynamic_bodies_iterator(rat_world *world);
rat_iterator *rat_world_arbiters_iterator(rat_world *world);

// the last argument, clean_blocks, says whether or not we should clean
// out the contact block recycler.  takes some extra time, but more memory
// efficient.  oh, what to chose!?
void rat_world_update(rat_world *world,rat_real timestep,int clean_blocks);

#ifdef __cplusplus
}
#endif

// this is used internally by rat_world_update; do not use
void rat_world_integrate(rat_world *world,rat_real step);

#endif
